﻿/* Copyright (c) 2010 Lexalytics Inc.

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;


// Needed for Office and Excel integration
using Excel = Microsoft.Office.Interop.Excel;

// Needed for business classes
using LexaScopeNET;
using Lexalytics4Excel.Dialogs;

namespace Lexalytics4Excel.SheetHelpers
{
    /// <summary>
    /// Class for performing representation of Theme results in Excel worksheet
    /// </summary>
    public class ThemeHelper
    {
        private DataProject _themeProject;
        private Excel.Worksheet _wsThemes;
        private string _entityTabName = "Themes";

        private int _nCurrentThemeNum = 1;
        private int _nCurrentSheetRow = 1;
        private int _kMinEntityDetailsRows = 5;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="ThemeDataProject">DataProject containing data and results for display</param>
        public ThemeHelper(DataProject ThemeDataProject)
        {
            _themeProject = ThemeDataProject;
        }

        /// <summary>
        /// Public method to generate results
        /// </summary>
        /// <returns>Integer return code</returns>
        public int GenerateResultsSheet()
        {
            int nRetCode = 0;
            nRetCode = CreateThemesWorksheet();
            if (nRetCode == 0)
            {
                GenerateTopThemesHeader("","");
            }
            return nRetCode;
        }

        /// <summary>
        /// Public method to generate results
        /// </summary>
        public int GenerateResultsSheet(string FilterColumn)
        {
            int nRetCode = 0;
            nRetCode = CreateThemesWorksheet();
            if (nRetCode == 0)
            {
                GenerateTopThemesHeader(FilterColumn, "");
            }
            return nRetCode;
        }

        /// <summary>
        /// Public method to generate results
        /// </summary>
        public int GenerateResultsSheet(string FilterColumn, string FilterValue)
        {
            int nRetCode = 0;
            nRetCode = CreateThemesWorksheet();
            if (nRetCode == 0)
            {
                GenerateTopThemesHeader(FilterColumn, FilterValue);
            }
            return nRetCode;
        }

        /// <summary>
        /// Adds current results into results sheet
        /// </summary>
        public void GenerateResultsSection()
        {
            InsertThemeResults("");
        }

        /// <summary>
        /// Adds current results into results sheet
        /// </summary>
        /// <param name="FilterValue">Current value for filtering</param>
        public void GenerateResultsSection(string FilterValue)
        {
            InsertThemeResults(FilterValue);
        }

        /// <summary>
        /// Creates the worksheet for theme results, deleting any existing if confirmed by user.
        /// </summary>
        /// <returns>Integer status code</returns>
        private int CreateThemesWorksheet()
        {
            // Retain name of current worksheet so we can return to it
            Excel.Application _excel = Globals.LexalyticsAddIn.Application;
            Excel.Workbook _book = _excel.ActiveWorkbook;


            foreach (Excel.Worksheet ws in _book.Sheets)
            {
                if (ws.Name == _entityTabName)
                {
                    ResultsSheetCreate dlgCreateResults = new ResultsSheetCreate(_entityTabName, _book);
                    if (dlgCreateResults.ShowDialog() == DialogResult.Cancel)
                        return -1;
                    else
                    {
                        if (!dlgCreateResults.OverwriteTab)
                            _entityTabName = dlgCreateResults.TabName;
                        else
                            ws.Delete();
                        break;
                    }
                }
            }

            _wsThemes = (Excel.Worksheet)Globals.LexalyticsAddIn.Application.Sheets.Add(Type.Missing, Type.Missing, Type.Missing, Excel.XlSheetType.xlWorksheet);
            _wsThemes.Name = _entityTabName;
            _wsThemes.Visible = Excel.XlSheetVisibility.xlSheetVisible;

            return 0;
        }

        /// <summary>
        /// Generates sheet output of the top themes retrieved from results
        /// </summary>
        /// <param name="FilterColumn">String containing name of sort or filter column, if applicable</param>
        /// <param name="FilterValue">String containing filter value, if applicable</param>
        private void GenerateTopThemesHeader(string FilterColumn, string FilterValue)
        {
            if (_wsThemes == null)
                return;

            // Set header
            Excel.Range r = _wsThemes.get_Range(_wsThemes.Cells[1, "A"],_wsThemes.Cells[1, "H"]);
            r.Merge(true);
            r.Value2 = "Top Themes";
            if (!FilterColumn.Equals("") && !FilterValue.Equals(""))
                r.Value2 = String.Format("Top Themes ({0} equals {1})", FilterColumn, FilterValue);
            else if (!FilterColumn.Equals("") && (FilterValue.Equals("")))
                r.Value2 = String.Format("Top Themes (sorted by {0})", FilterColumn);
            else
                r.Value2 = "Top Themes";
            r.Font.Bold = true;
            r.Font.Size = 14;
            r.Font.Name = "Calibri";
            r.WrapText = false;
            r.Interior.Color = ColorTranslator.ToOle(System.Drawing.Color.FromArgb(216, 216, 216));

            // Set subheader
            r = (Excel.Range)_wsThemes.Cells[2, "A"];
            r.Value2 = "Theme text";
            r.ColumnWidth = 42;
            r.Font.Italic = true;
            r.Font.Size = 11;
            r.Font.Name = "Calibri";
            r.WrapText = false;

            r = (Excel.Range)_wsThemes.Cells[2, "B"];
            r.Value2 = "# of documents";
            r.ColumnWidth = 13;
            r.Font.Italic = true;
            r.Font.Size = 11;
            r.Font.Name = "Calibri";
            r.WrapText = false;

            if (!String.IsNullOrEmpty(FilterColumn) && (String.IsNullOrEmpty(FilterValue)))
            {
                r = (Excel.Range)_wsThemes.Cells[2, "C"];
                r.Value2 = FilterColumn;
                r.ColumnWidth = 13;
                r.Font.Italic = true;
                r.Font.Size = 11;
                r.Font.Name = "Calibri";
                r.WrapText = false;
            }

            _nCurrentSheetRow = 3;
            _nCurrentThemeNum = 1;
        }

        /// <summary>
        /// Inserts a block of entity results, separated from creation of header to allow results to be added on.
        /// </summary>
        /// <param name="FilterValue">Current value for filtered results</param>
        private void InsertThemeResults(string FilterValue)
        {
            foreach (int nTheme in _themeProject.ThemeResults.Keys)
            {
                LXATheme myTheme = (LXATheme)_themeProject.ThemeResults[nTheme];
                Excel.Range r = (Excel.Range)_wsThemes.Cells[_nCurrentSheetRow, "A"];
                r.Font.Size = 10;
                r.Font.Name = "Calibri";
                r.WrapText = false;
                r.Value2 = myTheme.ThemeText;

                string rangeName = String.Format("LXAEntityDetails_Theme{0}", _nCurrentThemeNum);
                r.Hyperlinks.Add(r, "", String.Format("{0}!{1}", _wsThemes.Name, rangeName), Type.Missing, Type.Missing);

                r = (Excel.Range)_wsThemes.Cells[_nCurrentSheetRow, "B"];
                r.Font.Size = 10;
                r.Font.Name = "Calibri";
                r.WrapText = false;
                r.Value2 = myTheme.MentionList.Count.ToString();

                if (!String.IsNullOrEmpty(FilterValue))
                {
                    r = (Excel.Range)_wsThemes.Cells[_nCurrentSheetRow, "C"];
                    r.Font.Size = 10;
                    r.Font.Name = "Calibri";
                    r.WrapText = false;
                    r.Value2 = FilterValue;
                }

                _nCurrentSheetRow++;
                _nCurrentThemeNum++;
                InsertThemeMentions(myTheme, rangeName, ref _nCurrentSheetRow);
            }
        }

        /// <summary>
        /// Inserts the individual mentions for a given entity
        /// </summary>
        /// <param name="aTheme">The theme object containing the mentions list</param>
        /// <param name="rangeName">String to identify Excel NamedRange to create for details</param>
        /// <param name="nCurrentRow">Row of sheet to start on</param>
        private void InsertThemeMentions(LXATheme aTheme, string rangeName, ref int nCurrentRow)
        {
            int nBeginRow = nCurrentRow;

            // Set header
            Excel.Range r = (Excel.Range)_wsThemes.Cells[nCurrentRow, "B"];
            r.Value2 = "Document";
            r.ColumnWidth = 13;
            r.Font.Italic = true;
            r.Font.Size = 11;
            r.Font.Name = "Calibri";
            r.WrapText = false;

            r = (Excel.Range)_wsThemes.Cells[nCurrentRow, "C"];
            r.Value2 = "Sentiment";
            r.ColumnWidth = 13;
            r.Font.Italic = true;
            r.Font.Size = 11;
            r.Font.Name = "Calibri";
            r.WrapText = false;

            foreach (LXAThemeMention aMention in aTheme.MentionList)
            {
                string targetRow = null;
                if (aMention.ThemeMetadata.ContainsKey("rowindex"))
                    targetRow = aMention.ThemeMetadata["rowindex"];
                bool bRowColor = false;
                if (Lexalytics4Excel.Properties.Settings.Default["RowColoring"] != null)
                    bRowColor = Convert.ToBoolean(Lexalytics4Excel.Properties.Settings.Default["RowColoring"]);

                if (!String.IsNullOrEmpty(targetRow))
                {
                    nCurrentRow++;
                    r = (Excel.Range)_wsThemes.Cells[nCurrentRow, "B"];
                    r.Font.Size = 10;
                    r.Font.Name = "Calibri";
                    r.WrapText = false;
                    r.Value2 = String.Format("Row {0}", targetRow);
                    r.Hyperlinks.Add(r, "", String.Format("'{0}'!{1}{2}", _themeProject.UserRangeWorksheet, _themeProject.UserRangeTextColumn, targetRow), Type.Missing, Type.Missing);

                    r = (Excel.Range)_wsThemes.Cells[nCurrentRow, "C"];
                    r.Font.Size = 8;
                    r.Font.Name = "Calibri";
                    r.Font.Italic = true;
                    r.WrapText = false;
                    r.Value2 = aMention.Magnitude;

                    // Set theme mention style
                    r = _wsThemes.get_Range(_wsThemes.Cells[nCurrentRow, "B"], _wsThemes.Cells[nCurrentRow, "C"]);
                    r.Font.Size = 10;
                    r.Font.Name = "Calibri";
                    r.WrapText = false;
                    if (bRowColor)
                    {
                        if (aMention.Sentiment > 0)
                            r.Cells.Interior.ColorIndex = 43;
                        else if (aMention.Sentiment < 0)
                            r.Cells.Interior.ColorIndex = 3;
                        else
                            r.Cells.Interior.ColorIndex = 33;
                    }
                }
            }

            // Ensure that theme details have minimum of 5 rows
            if (nCurrentRow - nBeginRow < _kMinEntityDetailsRows)
                nCurrentRow = nBeginRow + _kMinEntityDetailsRows;

            // Insert sentiment pie chart
            InsertThemeSentimentChart(aTheme, nBeginRow, nCurrentRow);

            // Name the range
            r = (Excel.Range)_wsThemes.get_Range(String.Format("A{0}", nBeginRow), String.Format("C{0}", nCurrentRow));
            Excel.Name themeDetails = _wsThemes.Names.Add(rangeName, r, true,
                                            Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                                            Type.Missing, Type.Missing, Type.Missing, Type.Missing);
            r.EntireRow.Hidden = true;
            nCurrentRow++;
        }

        /// <summary>
        /// Inserts the pie chart of theme sentiment across mentions
        /// </summary>
        /// <param name="aTheme">The theme</param>
        /// <param name="nBeginRow">Beginning row of theme details section</param>
        /// <param name="nEndRow">End row of theme details section</param>
        private void InsertThemeSentimentChart(LXATheme aTheme, int nBeginRow, int nEndRow)
        {
            // Set header and labels
            Excel.Range r = (Excel.Range)_wsThemes.Cells[nBeginRow, "E"];
            r.Value2 = "Sentiment chart";
            r = (Excel.Range)_wsThemes.Cells[nBeginRow + 1, "E"];
            r.Value2 = "Neutral";
            r = (Excel.Range)_wsThemes.Cells[nBeginRow + 2, "E"];
            r.Value2 = "Negative";
            r = (Excel.Range)_wsThemes.Cells[nBeginRow + 3, "E"];
            r.Value2 = "Positive";

            // Tally up percentages
            int nPos = 0;
            int nNeg = 0;
            int nNeu = 0;
            int nTot = 0;

            foreach (LXAThemeMention aMention in aTheme.MentionList)
            {
                nTot++;
                if (aMention.Sentiment > 0)
                    nPos++;
                else if (aMention.Sentiment < 0)
                    nNeg++;
                else
                    nNeu++;
            }

            if (nPos + nNeg + nNeu > 0)
            {
                r = (Excel.Range)_wsThemes.Cells[nBeginRow + 1, "F"];
                r.Value2 = Convert.ToDouble(nNeu) / Convert.ToDouble(nTot);
                r = (Excel.Range)_wsThemes.Cells[nBeginRow + 2, "F"];
                r.Value2 = Convert.ToDouble(nNeg) / Convert.ToDouble(nTot);
                r = (Excel.Range)_wsThemes.Cells[nBeginRow + 3, "F"];
                r.Value2 = Convert.ToDouble(nPos) / Convert.ToDouble(nTot);

                // Insert basic pie chart object
                Excel.ChartObjects charts = (Excel.ChartObjects)_wsThemes.ChartObjects(Type.Missing);

                // Gets the cells that define the bounds of the data to be charted
                Excel.Range chartTopLeft = (Excel.Range)_wsThemes.Cells[nBeginRow, "D"];
                double chartX = Convert.ToDouble(chartTopLeft.Left);
                double chartY = Convert.ToDouble(chartTopLeft.Top);
                Excel.Range chartBottomRight = (Excel.Range)_wsThemes.Cells[nBeginRow + 5, "I"];
                double chartX2 = Convert.ToDouble(chartBottomRight.Left);
                double chartY2 = Convert.ToDouble(chartBottomRight.Top);

                Excel.ChartObject chartObj = charts.Add(chartX, chartY, chartX2 - chartX, chartY2 - chartY);
                Excel.Chart chart = chartObj.Chart;
                chart.ChartType = Excel.XlChartType.xlPie;

                // Select the data series
                Excel.SeriesCollection seriesCollection = (Excel.SeriesCollection)chart.SeriesCollection(Type.Missing);
                Excel.Range chartRange = _wsThemes.get_Range(String.Format("={0}!$E${1}:$F${2}", _wsThemes.Name, nBeginRow + 1, nBeginRow + 3), Type.Missing);
                chart.SetSourceData(chartRange, Type.Missing);

                // Format legend, data labels
                chart.HasLegend = false;
                chart.ApplyDataLabels(Microsoft.Office.Interop.Excel.XlDataLabelsType.xlDataLabelsShowLabelAndPercent, Type.Missing, Type.Missing, true, false, true, false, true, false, Type.Missing);
            }
        }
    }
}
